import chalk from "chalk";

import {
  accessKey,
  configVfs,
  prettyDisplayParts,
  prettyDocumentation,
} from "../../../utils/typescriptVfs.js";

export function main(options: { filename?: string }) {
  const { filename } = options;
  const { getCompletions, getQuickInfo } = configVfs({
    filename,
    initialCode: `const preset: GraphileConfig.Preset`,
  });

  let outputText = ``;
  let last = "";
  let inCode = false;
  function out(text = ""): void {
    if (text === "" && last === "" && !inCode) {
      return;
    }
    let exitCode = false;
    if (!inCode && text.startsWith("```")) {
      inCode = true;
    } else if (text.startsWith("```")) {
      exitCode = true;
    }
    outputText += (inCode ? chalk.green(text) : text) + "\n";
    last = text;
    if (exitCode) {
      inCode = false;
    }
  }
  out(chalk.whiteBright.bold(`# Custom \`GraphileConfig.Preset\` Reference`));
  out();
  out(`\
This reference was autogenerated by \`graphile config options\` using
your local configuration file to determine the plugins and presets (and
hence the configuration options) available. You should regenerate it
from time to time (for example, when you upgrade a module, or add/remove
modules).\
`);
  out();

  /*
  {
    const content = BASE_CONTENT;
    env.updateFile(FAKE_FILENAME, content);
    const info = env.languageService.getQuickInfoAtPosition(
      FAKE_FILENAME,
      content.length,
    );
    //out(prettyDocumentation(info?.documentation));
    //out();
  const content2 =
    BASE_CONTENT + `;\ntype Config = Digest<GraphileConfig.Preset>;`;
  env.updateFile(FAKE_FILENAME, content2);
  const info2 = env.languageService.getQuickInfoAtPosition(
    FAKE_FILENAME,
    content.length + 8,
  );

  out("```ts");
  //console.dir(info2);
  out(prettyDisplayParts(info2?.displayParts, "="));
  out("```");
  out();
  }
  */

  const keys: string[] = [];
  {
    const completions = getCompletions(" = {");
    if (completions?.entries) {
      for (const entry of completions.entries) {
        if (entry.kind === "property") {
          keys.push(entry.name);
        } else {
          console.error(
            `Did not understand '${entry.kind}' entry on GraphileConfig.Preset`,
          );
        }
      }
    }
    //console.dir(completions);
  }
  keys.sort();
  ["extends", "plugins", "disablePlugins"].reverse().forEach((key) => {
    const i = keys.indexOf(key);
    if (i >= 0) {
      keys.splice(i, 1);
    }
    keys.unshift(key);
  });

  let later: Array<string | undefined> = [];
  function outLater(str?: string): void {
    later.push(str);
  }
  const entries: string[] = [];
  for (const key of keys) {
    // Always an object, unless...
    const isArray = [
      "disablePlugins",
      "plugins",
      "extends",
      "pgServices",
    ].includes(key);

    if (isArray) {
      const info = getQuickInfo(` = [];\npreset${accessKey(key)}`);
      entries.push(
        `${chalk.cyanBright(key)}?: ${prettyDisplayParts(
          info?.displayParts,
          ":",
        )
          .replace(/\| undefined$/, "")
          .trim()}`,
      );
    } else {
      const SUFFIX1 = ` = {`;
      const SUFFIX2 = `};\n`;
      const withProperty = ` = Object.create(null);\npreset${accessKey(
        key,
      )}${SUFFIX1}${SUFFIX2}`;
      const info = getQuickInfo(withProperty, SUFFIX1.length + SUFFIX2.length);
      entries.push(
        `${chalk.cyanBright(key)}?: ${prettyDisplayParts(
          info?.displayParts,
          ":",
        )};`,
      );

      const completions = getCompletions(withProperty, SUFFIX2.length);
      const relevant = completions?.entries
        .filter((e) => e.kind === "property")
        .map((r) => r.name)
        .sort();
      outLater(chalk.whiteBright.bold(`## ${chalk.cyanBright.bold(key)}`));
      outLater();
      outLater(prettyDocumentation(info?.documentation));
      outLater();

      if (relevant) {
        const subentries: string[] = [];
        let laterStill: Array<string | undefined> = [];
        const outLaterStill = (line?: string): void => {
          laterStill.push(line);
        };
        for (const subkey of relevant) {
          const withSubpropertyAccess =
            withProperty + `preset${accessKey(key)}!${accessKey(subkey)}`;
          const info = getQuickInfo(withSubpropertyAccess);
          subentries.push(
            `${chalk.greenBright.bold(subkey)}?: ${prettyDisplayParts(
              info?.displayParts,
              ":",
            )
              .replace(/\| undefined$/, "")
              .trim()};`,
          );
          /*
        const def = env.languageService.getDefinitionAtPosition(
          FAKE_FILENAME,
          contentWithSubpropertyAccess.length,
        );
        const hints = env.languageService.provideInlayHints(
          FAKE_FILENAME,
          ts.createTextSpan(
            contentWithProperty.length,
            contentWithSubpropertyAccess.length - contentWithProperty.length,
          ),
          undefined,
        );
        const com = env.languageService.getDocCommentTemplateAtPosition(
          FAKE_FILENAME,
          contentWithProperty.length,
        );
        console.log(key, subkey, info, def, hints, com);
        */
          outLaterStill(
            chalk.whiteBright(
              `### ${chalk.cyanBright.bold(key)}.${chalk.greenBright.bold(
                subkey,
              )}`,
            ),
          );
          outLaterStill();
          const displayParts = prettyDisplayParts(info?.displayParts, ":");
          outLaterStill(
            `${chalk.greenBright.bold("Type")}: \`${
              displayParts
                ? chalk.whiteBright(displayParts)
                : chalk.gray("unknown")
            }\``,
          );
          outLaterStill();
          outLaterStill(prettyDocumentation(info?.documentation));
          outLaterStill();
        }

        if (subentries.length) {
          outLater("```ts");
          outLater(`{`);
          for (const entry of subentries) {
            outLater("  " + entry);
          }
          outLater("}");
          outLater("```");
          outLater();
        }
        for (const line of laterStill) {
          outLater(line);
        }
        laterStill = [];
      }
    }
    /*
  if (completions?.entries) {
    for (const entry of completions.entries) {
      if (entry.kind === "property") {
        keys.push(entry.name);
      }
    }
  }
*/
  }

  if (entries.length) {
    out("```ts");
    out(`{`);
    for (const entry of entries) {
      out("  " + entry);
    }
    out("}");
    out("```");
    out();
  }
  for (const line of later) {
    out(line);
  }
  later = [];
  return outputText.trim() + "\n";
}
